var kit = require('./kit')
var CONST = kit.CONST
/*
 * 此段代码是为了真实项目而做，并不适合学习
ADT
  VDOM 
    {
      type: vdom
      type2: rendered
      name: tag name
      attr:{
        id: identity
        style: style sheet
        attribute: attribute
        ...other
      }
      child:[VDOM | VTEXT]
      realdom: real dom
    }
    {
      type: vdom
      name: tag name
      attr:{
        id: identity
        style: style sheet
        attribute: attribute
        ...other
      },
      child:[VDOM | VTEXT]
    }
  VTEXT
    {
      type:vtext,
      val: the content
    }
    {
      type:vtext
      type2:rendered
      val: the content
      realdom: real dom
    }
  Patch
    {
      type: patch
      realdom: real dom
      action: remove | append | props_remove | replace | props_replace
      target: undefined | VDOM
    }
    Props_replace
      {
        type: patch
        realdom: real dom
        action: props_replace
        target: key of props 
        targetValue: value of props
      }
*/
/*      My virtual dom    */
// VDom -> Element
// 1 vdom 渲染为真实的dom
// 2 vdom 填充 realdom 
function renderVDOM(vdom){
  var doc = document
  if(kit.isVDOM(vdom)){
    var realdom = doc.createElement(vdom.name)
    for(key in vdom.attr){
      applyAttr(realdom,key,vdom.attr[key])
    }
    vdom.type2 = CONST.RENDERED
    vdom.realdom = realdom
    if(kit.hasChild(vdom)){
      for(var i = 0;i<vdom.child.length;i++){
        var childDom = renderVDOM(vdom.child[i])
        realdom.appendChild(childDom)
      }
      return realdom    
    }else{
      return realdom
    }
  }else if(kit.isVTEXT(vdom)){
    var t = doc.createTextNode(vdom.val)
    vdom.type2 = CONST.RENDERED
    vdom.realdom = t
    return t
  }else{
    throw '不支持的vdom'
  }
}

function removeAttr(dom,key){
  if(key === 'style'){
    dom.removeAttribute('style')
  }else if(key == 'id'){ // attribute 不在这里单独做处理
    dom.removeAttribute('id')
  }else{
    dom[key] = ''
    delete dom[key]
  }
}
function applyAttr(dom,key,val){
  if(key === 'style'){
    if(isString(val)){
      dom.setAttribute('style',val)
    }else{
      dom.setAttribute('style',toStyleString(val))
    }
  }else if(key === 'attribute'){
    for(var nkey in val){
      if(val[nkey] == null){
        dom.removeAttribute(nkey)
      }else{
        dom.setAttribute(nkey,val[nkey])
      }
    }
  }else if(key == 'id'){
    if(val == null){
      delete dom.id
    }else{
      dom.id = val
    }
  }else{
    if(key === 'className'){
      if(val){
        dom.className = val
      }else{
        void null
      }
    }else{
      dom[key]=val
    }
  }
  function toStyleString(val){
    var style = []
    for(var skey in val){
      style.push(skey+':'+val[skey]+';')
    }
    return style.join('')
  }
}

// VDOM  -> VDOM -> PATCH
function diff(old,newd){
  var patch = []
  if(kit.isRendered(old)){
    _diffaction(old,newd,patch)
    return patch
  }else{
    throw 'old 必须渲染过之后才可以进行diff'
  }

  // 为了性能  采用迭代递归的方式
  function _diffaction(old,newd,patch){
    if(kit.isVDOM(old)){
      if(kit.isVDOM(newd)){
        if(old.name == newd.name){
          // 如果不需要重新创建dom则需要设为rendered状态
          newd.type2 = CONST.RENDERED
          newd.realdom = old.realdom
          _diffProps(old,newd,patch)
          _processChild(old,newd,patch)
        }else{
           patch.push({
            type:CONST.PATCH,
            action:CONST.REPLACE,
            target:newd,
            realdom:old.realdom
          })
        }
      }else{
        patch.push({
          type:CONST.PATCH,
          action:CONST.REPLACE,
          target:newd,
          realdom:old.realdom
        })
      }
    }else if(kit.isVTEXT(old)){
      if(kit.isVTEXT(newd)){
        if(old.val == newd.val){
          // 如果不需要变更设为rendered状态
          newd.type2 = CONST.RENDERED
          newd.realdom = old.realdom
        }else{
          patch.push({
            type:CONST.PATCH,
            action:CONST.REPLACE,
            target:newd,
            realdom:old.realdom
          })
        }
      }else{
        patch.push({
          type:CONST.PATCH,
          action:CONST.REPLACE,
          target:newd,
          realdom:old.realdom
        })
      }
    }else{
      throw '不支持的类型'
    }
  }

  function _diffeq(a,b,deep){
    if(deep == 0){
      return true
    }else{
      if(isObject(a)&&isObject(b)){
        var allKey = {}
        for(var akey in a){
          allKey[akey] = true
        }
        for(var bkey in b){
          allKey[bkey] = true
        }
        for(var key in allKey){
          var diffresult =  _diffeq(a[key],b[key],deep-1)
          if(diffresult===true){
            continue
          }else{
            return false
          }
        }
      }else{
        return a === b
      }
    }
  }
  function _diffProps(old,newd,patch){
    for(var key in old.attr){
      if(key in newd.attr){
        if(_diffeq(old.attr[key],newd.attr[key],7)){
          void null
        }else{
          // 特殊属性特殊处理
          if(key == 'attribute'){
            var obj = old.attr.attribute
            for(var k in obj){
              if(k in newd.attr.attribute){
                void null
              }else{
                newd.attr.attribute[k] = null
              }
            }
            patch.push({
              type:CONST.PATCH,
              action:CONST.PROPS_REPLACE,
              realdom:old.realdom,
              target:key,
              targetValue: newd.attr.attribute
            })
          }else{
            patch.push({
              type:CONST.PATCH,
              action:CONST.PROPS_REPLACE,
              realdom:old.realdom,
              target:key,
              targetValue: newd.attr[key]
            })
          }
        }
      }else{
        if(key == 'attribute'){
          var obj = old.attr.attribute
          var attrObj = {}
          for(var k  in obj){
            attrObj[k] = null
          }
          patch.push({
            type:CONST.PATCH,
            action:CONST.PROPS_REPLACE,
            realdom:old.realdom,
            target:key,
            targetValue: attrObj
          })
        }else{
          patch.push({
            type:CONST.PATCH,
            action:CONST.PROPS_REMOVE,
            realdom:old.realdom,
            target:key
          })
        }
      }
    }
    for(var key in newd.attr){
      if(key in old.attr){
        void null
      }else{
        patch.push({
          type:CONST.PATCH,
          action:CONST.PROPS_REPLACE,
          realdom:old.realdom,
          target:key,
          targetValue: newd.attr[key]
        })
      }
    }
  }
  function _processChild(old,newd,patch){
    if(hasChild(old)){
      if(hasChild(newd)){
        var len = old.child.length==newd.child.length ? ['eq',old.child.length]
        : old.child.length<newd.child.length          ? ['old',old.child.length]
        : ['new',newd.child.length]

        for(var i=0;i<len[1];i++){
          _diffaction(old.child[i],newd.child[i],patch)
        }
        if(len[0]==='eq'){
          void null
        }else if(len[0]==='old'){
          for(var i=len[1];i<newd.child.length;i++){
            patch.push({
              type:CONST.PATCH,
              action:CONST.APPEND,
              target:newd.child[i],
              realdom:old.realdom
            })
          }
        }else{
          for(var i=len[1];i<old.child.length;i++){
            patch.push({
              type:CONST.PATCH,
              action:CONST.REMOVE,
              realdom:old.child[i].realdom
            })
          }
        }
      }else{
        for(var i=0;i++;i<old.child.length){
          patch.push({
            type:CONST.PATCH,
            action:CONST.REMOVE,
            realdom:old.child[i].realdom
          })
        }
      }
    }else{
      if(hasChild(newd)){
        for(var i=0;i<newd.child.length;i++){
          patch.push({
            type:CONST.PATCH,
            action:CONST.APPEND,
            target:newd.child[i],
            realdom:old.realdom
          })
        }
      }else{
        void null
      }
    }
  }
}


function updatePatch(patchs){
  for(var i=0;i<patchs.length;i++){
    var p = patchs[i]
    _fn(p)
  }
  function _fn(p){
    switch(p.action){
      case CONST.REPLACE:
          p.realdom.parentNode.replaceChild(renderVDOM(p.target),p.realdom)
        break
      case CONST.REMOVE:
        p.realdom.parentNode.removeChild(p.realdom)
        delete p.realdom
        break
      case CONST.APPEND:
        p.realdom.appendChild(renderVDOM(p.target))
        break
      case CONST.PROPS_REMOVE:
        removeAttr(p.realdom,p.target)
        break
      case CONST.PROPS_REPLACE:
        applyAttr(p.realdom,p.target,p.targetValue)
        break
      default:
        void null
    }
  }
}

module.exports = {
  updatePatch:updatePatch,
  diff:diff,
  renderVDOM:renderVDOM
}

